using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Xml;
using Universe.Farm.Service.Mesages;

namespace Universe.Farm.Service
{
    class ServiceListenerAgent: FarmAgent
    {
        private IServiceHealthListener _service;
        private ServiceListenerConfiguration _Configuration;
        private Thread _Thread;
        private ManualResetEvent _StopEvent = null;
        private ManualResetEvent _ForceEvent = null;
        private volatile bool _IsRunning;


        public ServiceListenerAgent(FarmState farmState, IServiceHealthListener service, ServiceListenerConfiguration configuration) 
            : base(farmState)
        {
            _service = service;
            _Configuration = configuration;
        }

        public string ServiceName
        {
            get
            {
                return string.IsNullOrEmpty(_Configuration.Id)
                           ? _service.DefaultID
                           : _Configuration.Id;
            }
        }

        public void Start()
        {
            _StopEvent = new ManualResetEvent(false);
            _ForceEvent = new ManualResetEvent(false);
            _Thread = new Thread(ThreadProc);
            _Thread.Name = "Server Farm Listener of '" + ServiceName + "'";
            _Thread.IsBackground = true;
            _Thread.Start();
        }

        public void Stop()
        {
            if (_Thread != null && _StopEvent != null)
            {
                _StopEvent.Set();
                if (!_Thread.Join(1000))
                    _Thread.Abort();

                _Thread = null;
                _StopEvent.Close();
                _StopEvent = null;
            }
        }

        public void PingAndAnnounce()
        {
            if (!_IsRunning)
                _ForceEvent.Set();
        }

        private void ThreadProc()
        {
            DateTime last = DateTime.Now;
            TimeSpan interval = !_Configuration.Interval.HasValue ? _service.DefaultPingInterval : _Configuration.Interval.Value;

            IStateStorage stateStorage = TheFarmState.Config.ResolveRequired<IStateStorage>();

            while (true)
            {
                _IsRunning = true;
                Stopwatch sw = new Stopwatch();
                sw.Start();
                bool isOK = false;
                object description = null;
                try
                {
                    description = _service.Ping;
                    isOK = description != null;
                }
                catch (Exception)
                {
                }

                TimeStampIO tio = new TimeStampIO(stateStorage, "Announce\\" + ServiceName);
                TimeStamp ts = tio.Value;
                try
                {
                    AnnounceMessage message = new AnnounceMessage(
                        ts,
                        Environment.MachineName,
                        isOK,
                        ServiceName, 
                        DescriptionBuffer.Create(_service.DescryptionType, description)
                        );

                    foreach (DeliveryAgent station in TheFarmState.DeliveryStations)
                        station.Deliver(message);

                }
                finally
                {
                    ts.Inc();
                    tio.Value = ts;
                    _IsRunning = false;
                }
                    
                if (_ForceEvent.WaitOne(0, false))
                    _ForceEvent.Reset();

                long msec = sw.ElapsedMilliseconds;
                Trace.WriteLine("Ping to '" + ServiceName + "' elapsed: " + msec + " msec, Interval: " + (interval.TotalMilliseconds).ToString("f0") + " msec");
                long delta = Math.Max(1L, (long) (interval.TotalSeconds * 1000L - msec));
                int reason = WaitHandle.WaitAny(new WaitHandle[] {_StopEvent, _ForceEvent }, (int) delta, false);
                if (reason == WaitHandle.WaitTimeout || reason == 1)
                {
                    continue;
                }
                else if (reason == 0)
                {
                    return;
                }
            }
        }

    }
}
